/*
 * DERECHOS DE AUTOR (C) 2014 POR Jose Carlos Palma.
 *
 * TODOS LOS DERECHOS RESERVADOS. NINGUNA PARTE  DE ESTE CÓDIGO FUENTE 
 * PUEDE SER REPRODUCIDA O TRANSMITIDA O UTILIZADA EN  CUALQUIER FORMA 
 * O POR CUALQUIER  MEDIO, YA SEA  ELECTRÓNICO O MECÁNICO,  INCLUYENDO 
 * EL FOTOCOPIADO, GRABACIÓN O POR CUALQUIER SISTEMA DE ALMACENAMIENTO 
 * O RECUPERACIÓN DE INFORMACIÓN SIN  EL PERMISO PREVIO Y  POR ESCRITO
 * DEL PROPIETARIO DE LOS DERECHOS DE AUTOR.
 */
package org.dwh.pivot;

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import java.io.File;
import java.util.logging.Level;

/**
 *
 * @author Jose Carlos Palma
 * @version v1.0
 */
public class Excel implements Closeable {

    /**
     * Objeto que contiene la referencia a la aplicación de Excel (vía API)
     */
    private ActiveXComponent xls = null;
    /**
     * Objeto en si de Excel.
     */
    private Dispatch xlo = null;
    /**
     * Objeto que representa los Workbooks de una aplicación de Excel.
     */
    private Dispatch wbs = null;
    /**
     * Objeto que representa un archivo de Excel asociado a una aplicación de
     * Excel.
     */
    private Dispatch wb = null;

    /**
     * Crea una instancia de Excel (Crea el ActiveXComponent y el objeto
     * Workbooks).
     *
     * @see com.jacob.activeX.ActiveXComponent
     * @see com.jacob.com.Dispatch
     */
    public Excel() {
        String cmm = "";
        try {
            cmm = "No se pudo cargar la aplicacion de Excel.";
            xls = new ActiveXComponent("Excel.Application");
            cmm = "No se pudo establecer DisplayAlerts en false.";
            Dispatch.put(xls, "DisplayAlerts", "False");
            cmm = "No se pudo obtener el objeto de Excel.";
            xlo = xls.getObject();
            cmm = "No se pudo obtener los Workbooks.";
            wbs = xls.getProperty("Workbooks").getDispatch();
        } catch (Exception ex) {
            throw new PivotatorlException(cmm, ex, IExitCode.EXCEL_APPLICATION);
        }


    }

    /**
     * Obtiene el PID del Excel.<BR> Nota: Necesita que la hoja de Excel
     * implemente la siguiente función (en un Module de VBA)
     * <PRE>
     * Attribute VB_Name = "pivotUtil"
     * #If VBA7 Then
     * Declare PtrSafe Function GetCurrentProcessId Lib "kernel32" () As LongPtr
     * #Else
     * Declare Function GetCurrentProcessId Lib "kernel32" () As Long
     * #End If
     * </PRE>
     *
     * @return el pid del Excel.
     */
    public int getPID() {
        String retVal = "-1";
        try {
            retVal = Dispatch.call(xls, "Run", "GetCurrentProcessId").toString();
        } catch (Exception ex) {
            Pivotator.log(Level.WARNING, "No se pudo obtener el PID de Excel {0}", ex.getMessage());
        }
        return Integer.parseInt(retVal);
    }

    /**
     * Abre un archivo de Excel.
     *
     * @param fileName
     */
    public void open(String fileName) {
        try {
            File f = new File(fileName);
            Pivotator.log(Level.INFO, "Abriendo el archivo de Excel \"{0}\"", f.getAbsolutePath());
            wb = Dispatch.call(wbs, "Open", f.getAbsolutePath()).toDispatch();
        } catch (Exception ex) {
            throw new PivotatorlException("No se pudo abrir el archivo.", ex, IExitCode.WORKBOOK_NOT_EXISTS);
        }
    }

    /**
     * Actualiza todas las PivotTables del cubo. Para que no se actualice una
     * PivotTable el nombre debe comenzar con
     *
     * @, y si quieren que todas las PivotTables de una hoja no se actualice el
     * nombre de la hoja debe de comenzar con
     * @.
     *
     */
    public void refresh() {

        //Obtiene todas coleccion de Sheet
        Dispatch sheets = Dispatch.call(wb, "Sheets").toDispatch();
        int sheetCount = Dispatch.get(sheets, "Count").getInt();

        Pivotator.log(Level.INFO, "Cantidad de hojas {0}", sheetCount);

        //Recorre todas los hojas
        for (int i = 1; i <= sheetCount; i++) {
            Dispatch sheet = Dispatch.call(sheets, "Item", new Variant(i)).toDispatch();
            String sheetName = Dispatch.get(sheet, "Name").getString();

            if (!sheetName.startsWith(System.getProperty("sheet.ignore", "@"))) {

                ////////////////////////////////////////////////////////////////

                Dispatch pvts = Dispatch.call(sheet, "PivotTables").toDispatch();
                int pvtCount = Dispatch.get(pvts, "Count").getInt();

                Pivotator.log(Level.INFO, "  Actualizando las {0} pivot de la hoja \"{1}\"", new Object[]{pvtCount, sheetName});

                //Recorre todas las Pivottables en una hoja especifica
                for (int j = 1; j <= pvtCount; j++) {
                    Dispatch pvt = Dispatch.call(pvts, "Item", new Variant(j)).toDispatch();
                    String pvtName = Dispatch.get(pvt, "Name").getString();

                    if (!pvtName.startsWith(System.getProperty("pivot.ignore", "@"))) {
                        Pivotator.log(Level.INFO, "    Actualizando la Pivottable \"{0}\"", pvtName);
                        Dispatch.call(pvt, "RefreshTable");
                    } else {
                        Pivotator.log(Level.INFO, "    PivotTable \"{0}\" Ignorada...", pvtName);
                    }

                }
                
                ////////////////////////////////////////////////////////////////
                
                Dispatch objs = Dispatch.call(sheet, "ListObjects").toDispatch();
                int objCount = Dispatch.get(objs, "Count").getInt();
                
                Pivotator.log(Level.INFO, "  Actualizando las {0} QueryTables de la hoja \"{1}\"", new Object[]{objCount, sheetName});
                
                //Recorre todas las QueryTables en una hoja especifica
                for (int j = 1; j <= objCount; j++) {
                    
                    
                    Dispatch qry = Dispatch.call(objs, "Item", new Variant(j)).toDispatch();
                    String qryName = Dispatch.get(qry, "Name").getString();
                    
                    if( Dispatch.get(qry, "SourceType").getInt() != 3 ) { continue;}
                    
                    if (!qryName.startsWith(System.getProperty("pivot.ignore", "@"))) {
                        Pivotator.log(Level.INFO, "    Actualizando el QueryTable \"{0}\"", qryName);
                        Dispatch.call(qry, "Refresh");
                    } else {
                        Pivotator.log(Level.INFO, "    PivotTable \"{0}\" Ignorada...", qryName);
                    }

                }


            } else {
                Pivotator.log(Level.INFO, "  \"{0}\" Ignorada...", sheetName);
            }

        }
    }

    @Override
    public void close() {
        Pivotator.log(Level.INFO, "Cerrando la aplicacion de Excel...");
        try {
            if (wbs != null) {

                Dispatch.call(wbs, "Close");
            }
            wbs = null;
        } catch (Exception ex) {
            Pivotator.log(Level.WARNING, "No se pudo cerrar los Workboos", ex);
        }
        if (xlo != null) {
            xlo.safeRelease();
            xlo = null;
        }
    }

    /**
     * Indica si el archivo está en modo de "Solo Lectura"
     *
     * @return true si el archivo está en modo de "Solo Lectura"
     */
    public boolean isReadOnly() {
        if (wb == null) {
            return true;
        }
        return Dispatch.get(wb, "ReadOnly").getBoolean();
    }

    /**
     * Guarda los cambios del archivo de Excel.
     */
    public void save() {
        if (wb != null) {
            Dispatch.call(wb, "Save");
        }

    }
}
